package com.md24housecall.view;

import com.md24housecall.activity.R;
import com.md24housecall.util.MyLog;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug.ExportedProperty;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;


public class PagingListView extends ListView implements OnScrollListener {

	public interface OnXScrollListener extends OnScrollListener {
		public void onXScrolling(View view);
	}
	/**
	 * implements this interface to get refresh/load more event.
	 */
	public interface PagingListViewListener {
		public void onLoadMore();

		public void onRefresh();
	}
	private float					mLastY					= -1;		// save event y

	private Scroller				mScroller;							// used for scroll back

	private OnScrollListener		mScrollListener;					// user's scroll listener
	// the interface to trigger refresh and load more.
	private PagingListViewListener	mListViewListener;
	// -- header view
	private PagingListHeaderView	mHeaderView;
	// header view content, use it to calculate the Header's height. And hide it
	// when disable pull refresh.
	private RelativeLayout			mHeaderViewContent;
	private TextView				mHeaderTimeView;
	private int						mHeaderViewHeight;					// header view's height

	private boolean					mEnablePullRefresh		= true;
	private boolean					mPullRefreshing			= false;	// is refreshing.
	// -- footer view
	private PagingListFooterView	mFooterView;
	private boolean					mEnablePullLoad			= true;

	private boolean					mPullLoading;

	private boolean					mIsFooterReady			= false;
	// total list items, used to detect is at the bottom of listview.
	private int						mTotalItemCount;
	// for mScroller, scroll back from header or footer.
	private int						mScrollBack;

	private final static int		SCROLLBACK_HEADER		= 0;
	private final static int		SCROLLBACK_FOOTER		= 1;
	private final static int		SCROLL_DURATION			= 400;		// scroll back duration

	private final static int		PULL_LOAD_MORE_DELTA	= 50;		// when pull up >= 50px

	// at bottom, trigger
																		// load more.
	private final static float		OFFSET_RADIO			= 1.8f;	// support iOS like pull
																		// feature.

	/**
	 * @param context
	 */
	public PagingListView(Context context) {
		super(context);
		initWithContext(context);
	}

	public PagingListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWithContext(context);
	}

	public PagingListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initWithContext(context);
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			if (mScrollBack == PagingListView.SCROLLBACK_HEADER) {
				mHeaderView.setVisiableHeight(mScroller.getCurrY());
			} else {
				mFooterView.setBottomMargin(mScroller.getCurrY());
			}
			postInvalidate();
			invokeOnScrolling();
		}
		super.computeScroll();
	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		try {
			super.dispatchDraw(canvas);
		} catch (IndexOutOfBoundsException e) {
			// samsung error
		}
	}

	private void initWithContext(Context context) {
		mScroller = new Scroller(context, new DecelerateInterpolator());

		super.setOnScrollListener(this);

		// init header view
		mHeaderView = new PagingListHeaderView(context);
		mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);
		mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);

		addHeaderView(mHeaderView);

		setPullRefreshEnable(mEnablePullRefresh);

		// init footer view
		mFooterView = new PagingListFooterView(context);
		setPullLoadEnable(mEnablePullLoad);

		// init header height
		mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			@SuppressWarnings("deprecation")
			@Override
			public void onGlobalLayout() {
				mHeaderViewHeight = mHeaderViewContent.getHeight();
				getViewTreeObserver().removeGlobalOnLayoutListener(this);
			}
		});
	}

	private void invokeOnScrolling() {
		if (mScrollListener instanceof OnXScrollListener) {
			OnXScrollListener l = (OnXScrollListener) mScrollListener;
			l.onXScrolling(this);
		}
	}

	@Override
	@ExportedProperty
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		// send to user's listener
		mTotalItemCount = totalItemCount;
		if (mScrollListener != null) {
			mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
		}
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (mScrollListener != null) {
			mScrollListener.onScrollStateChanged(view, scrollState);
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (mLastY == -1) {
			mLastY = ev.getRawY();
		}
		if (mFooterView.getState() == PagingListFooterView.STATE_LOADING) {
			return super.onTouchEvent(ev);
		}
		switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN:
				mLastY = ev.getRawY();
//				this.getParent().requestDisallowInterceptTouchEvent(false);
				break;
			case MotionEvent.ACTION_MOVE:
				if (mEnablePullLoad || mEnablePullRefresh) {
					final float deltaY = ev.getRawY() - mLastY;
					mLastY = ev.getRawY();
					if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
						// the first item is showing, header has shown or pull down.
						updateHeaderHeight(deltaY / PagingListView.OFFSET_RADIO);
						invokeOnScrolling();
					} else if (getLastVisiblePosition() == mTotalItemCount - 1 && (mFooterView.getBottomMargin() > 0 || deltaY < 0) && mEnablePullLoad) {
						// last item, already pulled up or want to pull up.
						updateFooterHeight(-deltaY / PagingListView.OFFSET_RADIO);
					}
				}

				break;
			default:
				mLastY = -1; // reset
				if (getFirstVisiblePosition() == 0) {
					// invoke refresh
					if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
						mPullRefreshing = true;
						mHeaderView.setState(PagingListHeaderView.STATE_REFRESHING);
						if (mListViewListener != null) {
							mListViewListener.onRefresh();
						}
					}
					resetHeaderHeight();
				} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
					// invoke load more.
					if (mEnablePullLoad && mFooterView.getBottomMargin() > PagingListView.PULL_LOAD_MORE_DELTA) {
						startLoadMore();
					}
					resetFooterHeight();
				}
				break;
		}
		return super.onTouchEvent(ev);
	}

	private void resetFooterHeight() {
		int bottomMargin = mFooterView.getBottomMargin();
		if (bottomMargin > 0) {
			mScrollBack = PagingListView.SCROLLBACK_FOOTER;
			mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, PagingListView.SCROLL_DURATION);
			invalidate();
		}
	}

	/**
	 * reset header view's height.
	 */
	private void resetHeaderHeight() {
		int height = mHeaderView.getVisiableHeight();
		if (height == 0) {
			return;
		}
		// refreshing and header isn't shown fully. do nothing.
		if (mPullRefreshing && height <= mHeaderViewHeight) {
			return;
		}
		int finalHeight = 0; // default: scroll back to dismiss header.
		// is refreshing, just scroll back to show all the header.
		if (mPullRefreshing && height > mHeaderViewHeight) {
			finalHeight = mHeaderViewHeight;
		}
		mScrollBack = PagingListView.SCROLLBACK_HEADER;
		mScroller.startScroll(0, height, 0, finalHeight - height, PagingListView.SCROLL_DURATION);
		// trigger computeScroll
		invalidate();
	}

	@Override
	protected void layoutChildren() {
		// TODO Auto-generated method stub
		try {
	        super.layoutChildren();
	    } catch (IllegalStateException e) {
	        MyLog.e("PaginglistView", "This is not realy dangerous problem");
	    }
	}
	@Override
	public void setAdapter(ListAdapter adapter) {
		// make sure XListViewFooter is the last footer view, and only add once.
		if (mIsFooterReady == false) {
			mIsFooterReady = true;
			addFooterView(mFooterView);
		}
		super.setAdapter(adapter);
	}

	@Override
	public void setOnScrollListener(OnScrollListener l) {
		mScrollListener = l;
	}

	public void setPagingListViewListener(PagingListViewListener l) {
		mListViewListener = l;
	}

	/**
	 * enable or disable pull up load more feature.
	 *
	 * @param enable
	 */
	public void setPullLoadEnable(boolean enable) {
		mEnablePullLoad = enable;
		if (!mEnablePullLoad) {
			mFooterView.hide();
			mFooterView.setOnClickListener(null);
		} else {
			mPullLoading = false;
			mFooterView.show();
			mFooterView.setState(PagingListFooterView.STATE_NORMAL);
			// both "pull up" and "click" will invoke load more.
			mFooterView.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					startLoadMore();
				}
			});
		}
	}

	/**
	 * enable or disable pull down refresh feature.
	 *
	 * @param enable
	 */
	public void setPullRefreshEnable(boolean enable) {
		mEnablePullRefresh = enable;
		if (!mEnablePullRefresh) { // disable, hide the content
			mHeaderViewContent.setVisibility(View.GONE);
		} else {
			mHeaderViewContent.setVisibility(View.VISIBLE);
		}
	}
	
	/**
	 * set last refresh time
	 *
	 * @param time
	 */
	public void setRefreshTime(String time) {
		mHeaderTimeView.setText(time);
	}

	private void startLoadMore() {
		mPullLoading = true;
		mFooterView.setState(PagingListFooterView.STATE_LOADING);
		if (mListViewListener != null) {
			mListViewListener.onLoadMore();
		}
	}

	/**
	 * stop load more, reset footer view.
	 */
	public void stopLoadMore() {
		if (mPullLoading == true) {
			mPullLoading = false;
			mFooterView.setState(PagingListFooterView.STATE_NORMAL);
		}
	}

	/**
	 * stop refresh, reset header view.
	 */
	public void stopRefresh() {
		if (mPullRefreshing == true) {
			mPullRefreshing = false;
			resetHeaderHeight();
		}
	}

	private void updateFooterHeight(float delta) {
		int height = mFooterView.getBottomMargin() + (int) delta;
		if (mEnablePullLoad && !mPullLoading) {
			if (height > PagingListView.PULL_LOAD_MORE_DELTA) { // height enough to invoke load
													// more.
				mFooterView.setState(PagingListFooterView.STATE_READY);
			} else {
				mFooterView.setState(PagingListFooterView.STATE_NORMAL);
			}
		}
		mFooterView.setBottomMargin(height);

		//		setSelection(mTotalItemCount - 1); // scroll to bottom
	}

	private void updateHeaderHeight(float delta) {
		mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());
		if (mEnablePullRefresh && !mPullRefreshing) { // æœªå¤„äºŽåˆ·æ–°çŠ¶æ€�ï¼Œæ›´æ–°ç®­å¤´
			if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
				mHeaderView.setState(PagingListHeaderView.STATE_READY);
			} else {
				mHeaderView.setState(PagingListHeaderView.STATE_NORMAL);
			}
		}
		setSelection(0); // scroll to top each time
	}
	
}
